//go:build windows
// +build windows

package main

import (
	// "errors"
	"encoding/base64"
	"io"
	"io/fs"
	"os"
	"syscall"

	// syscall "golang.org/x/sys/unix"
	"encoding/json"
	"time"

	// log "github.com/sirupsen/logrus"
	"golang.org/x/sys/windows"
)

var (
	SEEK_SET      = 2
	LINUX_FNOCTTY = 0x8000
)

func (fuser *Fuse) Access(path string, mode int) bool {
	// fuse.access()
	full_path := Full_path(path)
	// logger.Printf("%v %v", full_path, mode)
	// fp,err := os.OpenFile(full_path, mode, 0644)
	// defer fp.Close()
	fi, err := os.Stat(full_path)
	if err != nil {
		logger.Printf("%v %v", full_path, err)
		return false
	}
	if int(fi.Mode())&mode == mode {
		// logger.Printf("获得权限 %v", full_path)
		return true
	}
	// logger.Printf("失败 %v", full_path)
	return false
}

func (fuser *Fuse) Chmod(path string, mode int) interface{} {
	//  fuse.chmod()
	full_path := Full_path(path)
	err := os.Chmod(full_path, os.FileMode(mode))
	// logger.Printf("%v %v %v", full_path, mode, err)
	if err != nil {
		// logger.Printf("%v %v", full_path, err)
		// return false
		// e, _ := err.(*os.PathError)
		// syscallErr, _ := errors.Unwrap(err).(*os.SyscallError)
		return OSError(1, err.Error(), path)
	}
	return true
}

func (fuser *Fuse) Chown(path string, uid int, gid int) bool {
	//  fuse.chown()
	full_path := Full_path(path)
	err := os.Chown(full_path, uid, gid)
	// logger.Printf("%v %v %v %v", full_path, uid, gid, err)
	if err != nil {
		return false
	}
	return true
}

func convertWindowsModeToLinux(mode int) int {
	// 假设 Windows 模式存储在一个整数中
	linuxMode := mode & 0x3fff
	// 处理文件类型
	if mode&0x80000000 == 0 {
		linuxMode |= 0x8000 // 文件
	} else {
		linuxMode |= 0x4000 // 目录
	}
	return linuxMode
}

func (fuser *Fuse) Getattr(path string, fd int) dict {
	full_path := Full_path(path)
	fileInfo, err := os.Lstat(full_path)
	if err != nil {
		logger.Println("Error 文件不存在:", err)
		return OSError(2, err.Error(), path)
	}
	sys := fileInfo.Sys().(*syscall.Win32FileAttributeData)
	loadFileId := func(path string) (i syscall.ByHandleFileInformation, err error) {
		var pathp *uint16
		pathp, err = syscall.UTF16PtrFromString(path)
		if err != nil {
			return
		}
		attrs := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS | syscall.FILE_FLAG_OPEN_REPARSE_POINT)
		h, err1 := syscall.CreateFile(pathp, 0, 0, nil, syscall.OPEN_EXISTING, attrs, 0)
		if err1 != nil {
			return
		}
		defer syscall.CloseHandle(h)
		err = syscall.GetFileInformationByHandle(h, &i)
		if err != nil {
			return
		}
		// fs.vol = i.VolumeSerialNumber
		// fs.idxhi = i.FileIndexHigh
		// fs.idxlo = i.FileIndexLow
		return
	}

	ret, err1 := loadFileId(full_path)
	if err1 != nil {
		logger.Println("Error loadFileId:", err1)
		return OSError(1, err1.Error(), path)
	}
	logger.Println("Getattr", full_path)
	return dict{
		"st_mode":  convertWindowsModeToLinux(int(fileInfo.Mode())),
		"st_ino":   (ret.FileIndexHigh << 32) | ret.FileIndexLow,
		"st_dev":   ret.VolumeSerialNumber,
		"st_nlink": 1,
		"st_uid":   0,
		"st_gid":   0,
		"st_size":  fileInfo.Size(),
		"st_atime": sys.LastAccessTime.Nanoseconds() / 1000000000,
		"st_mtime": fileInfo.ModTime().Unix(),
		"st_ctime": sys.CreationTime.Nanoseconds() / 1000000000,
	}
}

func (fuser *Fuse) Readdir(path string, fd int) interface{} {
	//  fuse.readdir()
	full_path := Full_path(path)
	files, err := os.ReadDir(full_path)
	if err != nil {
		if os.IsNotExist(err) {
			return OSError(2, "No such file or directory", path)
		}
		return OSError(1, err.Error(), path)
	}
	var outs []string
	outs = append(outs, ".")
	outs = append(outs, "..")
	for _, file := range files {
		outs = append(outs, file.Name())
	}
	return outs
}

func (fuser *Fuse) Readlink(path string) interface{} {
	//  fuse.readlink()
	full_path := Full_path(path)
	pathname, err := os.Readlink(full_path)
	if err != nil {
		if os.IsNotExist(err) {
			return OSError(2, "No such file or directory", path)
		}
		return OSError(1, err.Error(), path)
	}
	// if pathname.startswith("/"):
	// 	return os.path.relpath(pathname, FUSE.ROOT)
	// else:
	// 	return pathname
	return pathname
}

func (fuser *Fuse) Mknod(path string, mode int, dev int) interface{} {
	//  fuse.mknod()
	// ret := syscall.Mknod(Full_path(path), uint32(mode), dev)
	return dict{}
}

func (fuser *Fuse) Rmdir(path string) interface{} {
	//  fuse.rmdir()
	full_path := Full_path(path)
	err := os.Remove(full_path)
	if err != nil {
		if os.IsNotExist(err) {
			// return OSError(2, "No such file or directory", path)
			// 不存在代表删除成功了
			return true
		}
		return OSError(1, err.Error(), path)
	}
	return true
}

func (fuser *Fuse) Mkdir(path string, mode int) interface{} {
	//  fuse.mkdir()
	err := os.Mkdir(Full_path(path), fs.FileMode(mode))
	if err != nil {
		if os.IsNotExist(err) {
			return OSError(2, "No such file or directory", path)
		}
		return OSError(1, err.Error(), path)
	}
	return true
}

func (fuser *Fuse) Statfs(path string) interface{} {

	var freeBytesAvailableToCaller, totalNumberOfBytes, totalNumberOfFreeBytes uint64
	var volumeNameBuffer [windows.MAX_PATH]uint16
	var fileSystemNameBuffer [windows.MAX_PATH]uint16
	var serialNumber uint32
	var maxComponentLength uint32
	var fileSystemFlags uint32

	var utf16Path *uint16
	if path == "C:" {
		utf16Path, _ = windows.UTF16PtrFromString("/")
	} else {
		utf16Path, _ = windows.UTF16PtrFromString(path)
	}

	err := windows.GetDiskFreeSpaceEx(utf16Path, &freeBytesAvailableToCaller, &totalNumberOfBytes, &totalNumberOfFreeBytes)
	if err != nil {
		logger.Printf("Error GetDiskFreeSpaceEx %s: %v\n", path, err)
		return OSError(1, err.Error(), path)
	}
	logger.Printf("分区 %s\\ \n", path)
	// logger.Printf("\tFree bytes available to caller: %d\n", freeBytesAvailableToCaller)
	// logger.Printf("\tTotal number of bytes: %d\n", totalNumberOfBytes)
	// logger.Printf("\tTotal number of free bytes: %d\n", totalNumberOfFreeBytes)
	// GetVolumeInfo(path)

	if err := windows.GetVolumeInformation(utf16Path,
		&volumeNameBuffer[0], uint32(len(volumeNameBuffer)),
		&serialNumber,
		&maxComponentLength,
		&fileSystemFlags,
		&fileSystemNameBuffer[0], uint32(len(fileSystemNameBuffer))); err != nil {
		logger.Printf("Error GetVolumeInformation %s %v\n", path, err)
		return OSError(1, err.Error(), path)
	}

	volumeName := windows.UTF16ToString(volumeNameBuffer[:])
	fileSystemName := windows.UTF16ToString(fileSystemNameBuffer[:])
	// fmt.Printf("分区 %s\n", path)
	logger.Printf("\tVolume Name: %s\n", volumeName)
	logger.Printf("\tFile System Name: %s\n", fileSystemName)
	logger.Printf("\tSerial Number: %d\n", serialNumber)
	logger.Printf("\tMax Component Length: %d\n", maxComponentLength)
	logger.Printf("\tFile System Flags: %d\n", fileSystemFlags)

	f_bsize := 4096
	f_frsize := 4096
	f_blocks := totalNumberOfBytes / uint64(f_bsize)
	f_bfree := totalNumberOfFreeBytes / uint64(f_bsize)
	f_bavail := freeBytesAvailableToCaller / uint64(f_bsize)
	f_files := -1
	f_ffree := -1
	f_favail := -1
	f_flag := 65472255
	f_namemax := 255

	return dict{
		"f_bsize":   f_bsize,
		"f_frsize":  f_frsize,
		"f_blocks":  f_blocks,
		"f_bfree":   f_bfree,
		"f_bavail":  f_bavail,
		"f_files":   f_files,
		"f_ffree":   f_ffree,
		"f_favail":  f_favail,
		"f_flag":    f_flag,
		"f_namemax": f_namemax,
	}
}

func (fuser *Fuse) Unlink(path string) interface{} {
	return fuser.Rmdir(path)
}

func (fuser *Fuse) Symlink(name string, target string) interface{} {
	//  fuse.symlink()
	err := os.Symlink(target, name)
	if err != nil {
		return OSError(1, err.Error(), name)
	}
	return true
}

func (fuser *Fuse) Rename(old string, new string) interface{} {
	//  fuse.rename()
	err := os.Rename(Full_path(old), Full_path(new))
	if err != nil {
		return OSError(1, err.Error(), new)
	}
	return true
}

func (fuser *Fuse) Link(target string, name string) interface{} {
	//  fuse.link()
	err := os.Link(Full_path(target), Full_path(name))
	if err != nil {
		return OSError(1, err.Error(), name)
	}
	return true
}

func (fuser *Fuse) Utimens(path string, timestamps []float64) interface{} {
	//  fuse.utimens()
	var times []time.Time
	for _, ts := range timestamps {
		times = append(times, time.Unix(int64(ts), 0))
	}
	err := os.Chtimes(Full_path(path), times[0], times[1])
	if err != nil {
		return OSError(1, err.Error(), path)
	}
	return true
}

func (fuser *Fuse) Open(path string, flags int) interface{} {
	//  fuse.open()
	full_path := Full_path(path)

	mode := os.O_RDONLY

	if (flags & LINUX_FNOCTTY) == LINUX_FNOCTTY {
		mode |= syscall.O_NOCTTY
	}
	if FileExists(full_path) {
		mode |= os.O_RDWR
	} else {
		mode |= os.O_CREATE
	}

	file, err := os.OpenFile(full_path, mode, 0777) // syscall.S_IRUSR | syscall.S_IWUSR | syscall.S_IWRITE
	if err != nil {
		return OSError(1, err.Error(), path)
	}
	fd := int(file.Fd())
	logger.Printf("Open %v mode=0x%X fd=%v", path, mode, fd)
	fuser.Fds[fd] = file
	return fd
}

func (fuser *Fuse) Create(path string, mode int, fi int) interface{} {
	logger.Printf("Create %v mode=0x%X fi=%v", path, mode, fi)
	full_path := Full_path(path)
	file, err := os.OpenFile(full_path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777) // Create(full_path)
	if err != nil {
		return OSError(1, err.Error(), path)
	}
	fd := int(file.Fd())
	fuser.Fds[fd] = file
	logger.Printf("Open %v mode=0x%X fd=%v", path, mode, fd)
	return fd
}

func (fuser *Fuse) Write(path string, buf string, offset int, fd int) interface{} {
	if _, ok := fuser.Fds[fd]; !ok {
		return OSError(1, "file not open", path)
	}
	file := fuser.Fds[fd]
	if _, err := file.Seek(int64(offset), io.SeekStart); err != nil {
		return OSError(1, err.Error(), path)
	}
	decodedBytes, _ := base64.StdEncoding.DecodeString(buf)
	count, err := file.Write(decodedBytes)
	if err != nil {
		return OSError(13, err.Error(), path)
	}
	return count
}

func (fuser *Fuse) Truncate(path string, length int, fd int) interface{} {
	//  fuse.truncate()
	// full_path := Full_path(path)
	// err := syscall.Truncate(full_path, int64(length))
	// if err != nil {
	// 	return OSError(1, err.Error(), path)
	// }
	return true
}

func (fuser *Fuse) Flush(path string, fd int) interface{} {
	if _, ok := fuser.Fds[fd]; !ok {
		return OSError(1, "file not open", path)
	}
	file := fuser.Fds[fd]
	if err := file.Sync(); err != nil {
		return true // OSError(1, err.Error(), path)
	}
	return true
}

func (fuser *Fuse) Release(path string, fd int) interface{} {
	//  fuse.release()
	// _ = syscall.Close(syscall.Handle(fd))
	if _, ok := fuser.Fds[fd]; !ok {
		return OSError(1, "file not open", path)
	}
	file := fuser.Fds[fd]
	defer func() {
		delete(fuser.Fds, fd)
	}()
	file.Close()
	return true
}

func (fuser *Fuse) Fsync(path string, fdatasync int, fd int) interface{} {
	//  fuse.fsync()
	return fuser.Flush(path, fd)
}

func main_test() {
	fuser := &Fuse{}
	// fuser.Access("fuse.go", 0)
	// fuser.Chmod("fuse.go", 0644)
	// fuser.Chown("fuse.go", 197108, 197108)
	attr := fuser.Getattr("/tmp/", 0)
	logger.Printf("attr: %+v", attr)

	links := fuser.Readlink("/etc/os-release")
	logger.Printf("attr: %+v", links)

	_ = fuser.Mkdir("/tmp/test/", 0777)
	// defer Rmdir("/tmp/test/")

	root := fuser.Statfs("/tmp")
	root, _ = json.Marshal(root)
	logger.Printf("root: %s", root)

	_ = fuser.Symlink("/tmp/test/test1", "/tmp/wick_bash_autocomplete.sh")
	_ = fuser.Link("/tmp/wick_bash_autocomplete.sh", "/tmp/test/hardlink")
	// defer Unlink("/tmp/test/hardlink")

	_ = fuser.Rename("/tmp/test/test1", "/tmp/test/test")
	// defer Unlink("/tmp/test/test")
	dirs := fuser.Readdir("/tmp/test/", 0)
	logger.Printf("/tmp/test: %+v", dirs)

	_ = fuser.Utimens("/tmp/ttyd.x86_64-1.7.4", []float64{1701849604.8191702, 1701849604.8191702})
	fd := fuser.Open("/tmp/test/test", 2).(int)
	defer fuser.Release("/tmp/test/test", fd)

	txt := fuser.Read("", 200, 0, fd).(string)
	decodedBytes, _ := base64.StdEncoding.DecodeString(txt)
	logger.Printf("Read: %s", decodedBytes)
	fuser.Write("/tmp/test/test", txt, 50, fd)
	fuser.Flush("/tmp/test/test", fd)
	// Truncate("/tmp/test/test", 250, fd)

}
